<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>数组</title>
</head>
<body>
<p>1111</p>
<p>2222</p>
<p>3333</p>
<script src="./js/browser.js"></script>
<script type="text/babel">
    /*
	 * 数组解构赋值：
	 * ES6允许按照一定模式，从数组和对象中提取值，对变量进行赋值，这种被称为解构。
	 * 示例如下：
	 */
    var [a,b,c] = [1,2,3];
    console.log(a) // 1
    //本质上，这种写法属于模式匹配，只要等号两边的模式相同，左边的变量就会被赋予对应的值。
    //如果解构不成功，变量的值就等于undefined
    var [foo] = [];
    console.log(foo); //undefined

    var [bar,hoo] = [1];
    console.log(hoo); //undefined
    //上边这两种都属于解构不成功

    //下面情况属于不完全解构，但是会成功
   var [a,b]=[1,2,3];
   console.log([a,b]) // [1,2]

    //如果等号右边不是可以遍历的结构，那么将会报错：
    //let [foo]=1;//类似这种，因为转成对象后找不到Iterator接口。{}本身不具备Iterator接口

    //对于Set结构，也可以使用数组的解构赋值：
    let [x,y,z]=new Set(["one","two","three"]);
    console.log(x) //one

    //解构赋值允许有默认值
    var [foo = true] = [];
    console.log(foo);//true

    //如果默认值是一个表达式，那么这个表达式是惰性求值的，即只有在生效用到的时候才会求值。
    //默认值也可以引用结构赋值的其他变量，但该变量必须已经声明：
    let [p = 1,q=p] = [2];
    console.log([p,q]); //[2,2]



    //push():从后面添加元素，返回值为添加完后的数组的长度
    let arr=[1,2,3]
    console.log(arr.push(5)); //4
    console.log(arr); //[1, 2, 3, 5]

    //arr.pop(): 从后面删除元素，只能是一个，返回值是删除的元素
    console.log(arr.pop()); //5
    console.log(arr); //[1, 2, 3]

    //arr.shift() 从前面删除元素，只能删除一个 返回值是删除的元素
    console.log(arr.shift()); //1
    console.log(arr);//[2, 3]

    //arr.unshift() 从前面添加元素, 返回值是添加完后的数组的长度
    console.log(arr.unshift(22)); //3
    console.log(arr);//[22, 2, 3]

    //arr.splice(index,n) 删除从i(索引值)开始之后的那个元素。返回值是删除的元素
    //参数： index 索引值      n 个数
    console.log(arr.splice(0,2)); //[22, 2]
    console.log(arr) //[3]

    //arr.concat() 连接两个数组 返回值为连接后的新数组
    console.log(arr.concat([0,1,2,3])) //[3, 0, 1, 2, 3]
    console.log(arr) //[3]  返回的数组不会改变原数组
    let newarr=arr.concat([0,1,2,3])
    console.log(newarr) //[3, 0, 1, 2, 3]

    //str.split() 将字符串转化为数组
    let str='hello world'
    console.log(str.split('')) //["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]

    //arr.sort() 将数组进行排序,返回值是排好的数组，默认是按照最左边的数字进行排序，不是按照数字大小排序的
    let array=[10,-2,35,44,5]
    console.log(array.sort())  //[-2, 10, 35, 44, 5]
    let array1=array.sort((a,b)=>a-b)
    console.log(array1) //[-2, 5, 10, 35, 44]  升序
    let array2=array.sort((a,b)=>b-a)
    console.log(array2) //[44, 35, 10, 5, -2]  降序

    //arr.reverse() 将数组反转,返回值是反转后的数组，改变原数组
    let array3=[1,2,3,4,5]
    console.log(array3.reverse()) // [5, 4, 3, 2, 1]
    console.log(array3) // [5, 4, 3, 2, 1]

    // arr.slice(start,end) 切去索引值start到索引值end的数组，不包含end索引的值，返回值是切出来的数组,不改变原数组
    console.log(array3.slice(0,3))  //[5, 4, 3]
    console.log(array3)  //[5, 4, 3, 2, 1]

    //arr.forEach(callback) 遍历数组,无return
    //callback的参数： value --当前索引的值  index --索引  array --原数组
    array3.forEach( (value,index,array)=>{
        console.log(`value:${value}    index:${index}     array:${array}`)
        // value:5    index:0     array:5,4,3,2,1
        // value:4    index:1     array:5,4,3,2,1
        // value:3    index:2     array:5,4,3,2,1
        // value:2    index:3     array:5,4,3,2,1
        // value:1    index:4     array:5,4,3,2,1
    })

    //arr.map(callback) 映射数组(遍历数组),有return 返回一个新数组
    // callback的参数： value --当前索引的值  index --索引   array --原数组
    let array4=array3.map((value,index,array)=>{
        value=value*2;
        return value; //必须return才会返回值，否则返回undefined

        console.log(`value:${value}  index:${index}   array:${array}` )
        // value:10  index:0   array:5,4,3,2,1
        // value:8  index:1   array:5,4,3,2,1
        // value:6  index:2   array:5,4,3,2,1
        // value:4  index:3   array:5,4,3,2,1
        // value:2  index:4   array:5,4,3,2,1
    })
    console.log(array3)  //[5, 4, 3, 2, 1]
    console.log(array4)  //[10, 8, 6, 4, 2]
    //ps: arr.forEach()和arr.map()的区别
    // 1. arr.forEach()是和for循环一样，是代替for。arr.map()是修改数组其中的数据，并返回新的数据。
    // 2. arr.forEach() 没有return  arr.map() 有return

    //filter()：创建一个新数组，新数组中的元素是通过检查指定数组中符合条件的所有元素。返回一个满足要求的数组
    //对数组中的每个元素都执行一次指定的函数（callback），并且创建一个新的数组，该数组元素是所有回调函数执行时返回值为 true 的原数组元素。
    // 它只对数组中的非空元素执行指定的函数，没有赋值或者已经删除的元素将被忽略，同时，新创建的数组也不会包含这些元素。
    console.log([1,10,-5,8].filter(x=>x>0))  //[1, 10, 8]

    // arr.every(callback) 依据判断条件，数组的元素是否全满足，若满足则返回ture
    // let array5=array3.every((i,v)=>i<3)
    // console.log(array5) //false
    let array5=array3.every((i,v)=>i<6)
    console.log(array5) //true

    //arr.some() 依据判断条件，数组的元素是否有一个满足，若有一个满足则返回ture
    // let array6=array3.some((i,v)=>i<3)
    // console.log(array6) //true
    let array6=array3.some((i,v)=>i<0)
    console.log(array6) //false

    //arr.reduce(callback, initialValue) 迭代数组的所有项，累加器，数组中的每个值（从左到右）合并，最终计算为一个值
    //    参数： callback: previousValue 必选 --上一次调用回调返回的值，或者是提供的初始值（initialValue）
    //     currentValue 必选 --数组中当前被处理的数组项
    //     index 可选 --当前数组项在数组中的索引值
    //     array 可选 --原数组
    //     initialValue: 可选 --初始值
    // 实行方法：回调函数第一次执行时，preValue 和 curValue 可以是一个值，
    // 如果 initialValue 在调用 reduce() 时被提供，那么第一个 preValue 等于 initialValue ，
    // 并且curValue 等于数组中的第一个值；如果initialValue 未被提供，
    // 那么preValue 等于数组中的第一个值
    let array7=array3.reduce((preValue,curValue) =>preValue+curValue)
    console.log(array7)  //15

    // arr.indexOf() 查找某个元素的索引值，若有重复的，则返回第一个，查到的索引值若不存在，则返回 -1
    let array8=[1,2,3,3,4]
    console.log(array8.indexOf(3)) //2
    console.log(array8.indexOf(5)) //-1
    //arr.lastIndexOf()   和arr.indexOf()的功能一样，不同的是从后往前查找

    //Array.from() 将伪数组变成数组，就是只要有length的就可以转成数组。 ---es6
    //数组的扩展
    /*
     * Array.from()将两类对象转换为真正的数组：类似数组的对象和可遍历的对象
     * 包括es6新增数据类型Set和Map。
     * 下面是一个类似数组的对象，Array.from()将它转为真正的数组：
     */
    let arrLike = {"0":"a", "1":"b", "2":"c", length:3}
    //es5的写法
    var arr1 = [].slice.call(arrLike);
    console.log(arr1); //["a", "b", "c"]
    //es6 的写法
    var arr2 = Array.from(arrLike);
    console.log(arr2); //["a", "b", "c"]
    //实际应用中，常见的类似数组的对象是DOM操作返回的NodeList集合，以及函数内部arguments对象。
    //Array.from都可以将它们转换为数组。
    //nodelist对象
    let ps=document.querySelectorAll("p");
    Array.from(ps).forEach(function (p) {
        console.log(ps)  //返回一个nodelist对象  NodeList(3) [p, p, p]
    })

    //只要是部署了Iterator接口的数据，Array.from都可以将它转换为数组。
    console.log(Array.from("hello"));// ["h", "e", "l", "l", "o"]
    //arguments对象
    function test(a,...rest){
        var args = Array.from(arguments);
        console.log(args);  //[1, 2, 3, 4, 5, 6]
    }
    test(1,2,3,4,5,6);

    let nameSet = new Set(["a","b"]);
    console.log(Array.from(nameSet));

    //如果参数是一个真正的数组，Array.from会返回一个一模一样的新数组。
    //值得注意的是 扩展运算符(...)也可以将某些数据结构转为数组。
    function test2(){
        var args = [...arguments];
        console.log(args);//[2, 4, 5, 6]
    }
    test2(2,4,5,6);
    //nodelist对象
    console.log([...document.querySelectorAll("p")]); //[p,p,p]
    /*
            * 对于没有部署的浏览器可以用Array.prototype.slice方法替代。
            */
    // [].slice.call(arguments)能将具有length属性的对象转成数组
    const toArray = (() =>
            Array.from ? Array.from : obj=>[].slice.call(obj)
    )();
    // let toArray=function(a){
    //     try {
    //         return Array.from ? Array.from(a) : [].slice.call(a);
    //     }catch (e) {
    //         var arr=[];
    //         for (var i=0,len=a.length;i<len;i++){
    //             arr[i]=a[i]
    //         }
    //         return arr
    //     }
    // }
    console.log(toArray(arrLike))  //[1,2,3]
    //Array.from还可以接受第二个参数，作用类似于数组的map方法，用来对每个元素进行处理，
    //将处理后的值放入返回的数组。
    let num=[1,2,3]
    let arrLike1 = {"0":"a", "1":"b", "2":"c", length:3};
    console.log(Array.from(arrLike1,x=>x*x)); //[NaN, NaN, NaN]
    console.log(Array.from(num,x=>x*x)); //[1, 4, 9]
    //等同于
    console.log(Array.from(num).map(x=>x*x)); // [1, 4, 9]
    console.log(Array.from(arrLike).map(x=>x*x)); //[NaN, NaN, NaN]
    //用途比较广，dom操作，到数组值得转换，到返回一组数据的数据类型。
    function typesOf(){
        return Array.from(arguments,value=>typeof value)
    }
    console.log(typesOf("s",1,Number,[])) //["string", "number", "function", "object"]
    //只要有一个原始的数据结构，就可以对它的值进行处理，然后转成规范的数组，进而使用数组的方法。
    console.log(Array.from({length:2},()=>"jack"));  // ["jack", "jack"]


    //Array.of()将一组值，转换为数组。   将值转化为数组,且不接受参数
    console.log(Array.of(3,11,8));//[3, 11, 8]
    console.log(Array.of(3)); //[3]
    console.log(Array.of(3,11,8).length); //3
    //这个方法的主要目标是弥补数组构造函数的不足。因为参数个数不同，会导致Array()的行为有差异。
    console.log(Array());  //[]
    console.log(Array(3));  //[,,]
    console.log(Array(3,8,4)); //[3,8,4]
    // 上面代码中，Array方法参数为0，1，3时候，返回结果都不一样，只有当参数个数不少于2个时，Array才会返回
    //由参数组成的额数组，一个参数时，实际上是在指定数组长度。

    //Array.of() 基本上可以用来替代Array或new Array(), 并且不存在由于参数不同而导致的重载。行为非常统一。
    //Array.of()总是返回由参数值组成的数组。如果参数没有值，就返回一个空数组。
    //Array.of()可以用下面的代码模拟实现：
    function Arrayof(){
        return [].slice.call(arguments);
    }


     /*
      * 数组实例的copyWithin
      * 在当前的数组内部，将指定位置的成员复制到其他位置（会覆盖原有成员），然后返当前数组，也就是说，使用这个方法，会修改当前数组。
      * Array.prototype.copyWithin(target,start=0,end=this.length);
      * 接收三个参数：
      *    target：从该位置开始替换数据；
      *    start：从该位置开始读取数据，默认为0，如果为负值，表示倒数。
      *    end:到该位置停止读取数据，默认等于数组长度。如果是负值，表示倒数。
      * 这三个都应该是数值，如果不是，自动转换。
      */
    console.log([1,2,3,4,5].copyWithin(0,3)); //[4, 5, 3, 4, 5]   //将3号位复制到0号位

    // arr.find(callback) 找到第一个符合条件的数组成员
    //find()方法用于找出第一个符合条件的数组成员。它的参数是一个回调函数，
    //所有数组成员依次执行该回调函数，直到找出第一个返回值为true的成员，然后返回该成员。
    // 如果没有符合条件的则返回undefined
    //find()方法为数组中的每个元素都调用一次函数执行，当数组中的元素在测试条件时返回true，
    // find()返回符合条件的元素，之后的值不会再执行函数。如果没有符合条件的元素则返回undefined。
    //find的回调函数有三个参数，值，索引，原数组
    console.log([1,2,-5,7].find(x=>x<0));  //-5
    console.log([1,2,-5,7].find(function (value,index,arr) {
        return value>0
    })) //1

    //findIndex方法的用法与find方法非常类似，返回一个符合条件的数组成员的索引，都不符合就返回-1。
    console.log([1,2,-5,7].findIndex(function (value,index,arr) {
        return value>10
    }))  //-1

    //arr.fill(target, start, end) 使用给定的值，填充一个数组,ps:填充完后会改变原数组
    // 参数： target -- 待填充的元素
    // start -- 开始填充的位置-索引
    // end -- 终止填充的位置-索引（不包括该位置)
    let array9=[1,2,3,4,5]
    let array10=array9.fill(5)
    console.log(array9) //[5, 5, 5, 5, 5]
    console.log(array10) //[5, 5, 5, 5, 5]
    let arr3=[1,2,3,4,5]
    // let arr4 = arr3.fill(5,2)
    // console.log(arr4) //[1, 2, 5, 5, 5]
    let arr5 = arr3.fill(5,1,3)
    console.log(arr5) // [1, 5, 5, 4, 5]


    //arr.includes() 判断数中是否包含给定的值
    console.log(arr5.includes(5))  //true
    let arr6=[NaN,1,2,3]
    console.log(arr6.includes(NaN)) //true
    console.log(arr6.includes(6)) //false
    //ps:与indexOf()的区别：
    //1 indexOf()返回的是数值，而includes()返回的是布尔值
    //2 indexOf() 不能判断NaN，返回为-1 ，includes()则可以判断


     /*
      * 数组实例的entries(),keys(),values()用于遍历数组。
      * 都返回一个Iterator对象，用for of循环，区别是
      * keys()是对键名的遍历，values()是对键值的遍历，entries是对键值对的遍历。
      */
    //arr.keys() 遍历数组的键名
    let arr7=[1,2,3,4]
    for (let key of arr7){
        console.log(key) //1、2、3、4
    }

    //arr.values() 遍历数组键值
    for (let val of arr7){
        console.log(val) //1、2、3、4
    }

    //arr.entries() 遍历数组的键名和键值
    for (let e of arr7){
        console.log(e)  //1、2、3、4
    }

    //如果不使用for of循环，可以用手动的next进行遍历：
    let letter = ["a","b","v"];
    let entries = letter.entries();
    console.log(entries.next().value);
    console.log(entries.next().value);
    console.log(entries.next().value);
    console.log(entries.next().value); //超过了长度就是undefined

    /*
		 * 数组的空位：
		 * 空位不是undefined，一个位置的值等于undefined，依然是有值的。
		 * 空位是米有任何值的。
		 */
    console.log( 0 in [undefined]);  //true
    console.log( 0 in [,]);    //false
    /*
     * 上面代码说明，第一个数组的0号位置是有值的，第二个数组的0号位是没有值的。
     * es5对空位的处理很不一致，大多数情况，跳过空位。
     * forEach(),filter(),every(),some()都会跳过空位。
     * map()会跳过空位，但会保留这个值
     * join()和toString()会将空位视为undefined，而undefined和null会被
     * 处理成空字符串。
     *
     * es6明确将空位转为undefined。
     * Array.from会将数组的空位转为undefined，也就是不会忽略空位。
     */
    console.log(Array.from(["a",,"b"])); //[a,undefined,b]
    //扩展运算符也将空位转为undefined
    console.log([...["a",,"b"]]);  //[1,undefined,2]
    //上面代码实测的时候，结果是[a,2:b]; 并没有undefined。


    //数组去重
    function dedupe(array) {
        return [...new Set(array)]
    }
    var arr = [1,2,2,3,3,4,4,5,5]
    console.log(dedupe(arr))  //[1, 2, 3, 4, 5]
</script>
</body>
</html>